package fs

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/burybell/images-bed/tool"
	"github.com/syndtr/goleveldb/leveldb"
	"io"
	"io/fs"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var (
	banner string
)

func init() {
	err := filepath.Walk("./", func(path string, info fs.FileInfo, err error) error {
		if !info.IsDir() && strings.Contains(path, "banner") {
			file, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			if file != nil && len(file) > 0 {
				banner = string(file)
			}
			return nil
		}
		return nil
	})
	if err != nil {
		log.Printf("banner load err = %v\n", err)
	}
}

func GetBanner() string {
	return banner
}

// Image 图片文件
type Image struct {
	Size       int64  `json:"size"`
	FileName   string `json:"fileName"`
	Scene      string `json:"scene"`
	Suffix     string `json:"suffix"`
	Md5        string `json:"md5"`
	Path       string `json:"path"`
	CreateTime int64  `json:"createTime"`
	UpdateTime int64  `json:"updateTime"`
}

var (
	db          *leveldb.DB
	basePath    string
	scenes      map[string]bool
	upperSuffix map[string]bool
)

func OpenFs(dataPath string, suffixs []string) {
	var err error

	// 创建base文件夹，如果不存在
	base, err := tool.GetPathOrCreate(dataPath)
	if err != nil {
		panic("init fs error")
	}

	// 打开文件系统数据库
	db, err = leveldb.OpenFile(tool.GetPathWithBase(base, "_db"), nil)
	if err != nil {
		panic("open db error")
	}

	// 创建图片目录
	basePath, err = tool.GetPathOrCreate(base, "_images")
	if err != nil {
		panic("open data dir error")
	}

	scenes = make(map[string]bool)

	dirs, err := ioutil.ReadDir(basePath)
	if err != nil {
		panic("get scenes dir error")
	}

	// 初始化场景
	for i := 0; i < len(dirs); i++ {
		if dirs[i].IsDir() {
			scenes[dirs[i].Name()] = true
		}
	}

	// 初始化可上传的图片类型
	upperSuffix = make(map[string]bool)
	for i := 0; i < len(suffixs); i++ {
		upperSuffix[strings.ToUpper(suffixs[i])] = true
	}
}

// CreateScene 创建场景,存在的不创建
func CreateScene(scene string) error {
	dir, err := tool.GetPathOrCreate(basePath, scene)
	if err != nil {
		return err
	}
	fmt.Printf("create scene = %s", dir)
	scenes[scene] = true
	return nil
}

// HasScene 判断场景是否存在
func HasScene(scene string) bool {
	if _, ok := scenes[scene]; ok {
		return true
	}
	return false
}

// DeleteScene 删除场景
func DeleteScene(scene string) {
	if HasScene(scene) {
		image := new(Image)
		iter := db.NewIterator(nil, nil)
		for iter.Next() {
			err := json.Unmarshal(iter.Value(), image)
			if err != nil {
				// 废数据，清理
				err := db.Delete(iter.Key(), nil)
				if err != nil {
					continue
				}
			} else {
				if image.Scene == scene {
					err := DeleteImage(image)
					if err != nil {
						continue
					}
				}
			}
			image.Scene = ""
		}
		delete(scenes, scene)
		// 删除文件夹
		err := os.RemoveAll(tool.GetPathWithBase(basePath, scene))
		if err != nil {
			return
		}
	}
}

// HasSuffix 判断是否存在允许图片类型
func HasSuffix(suffix string) bool {
	if _, ok := upperSuffix[strings.ToUpper(suffix)]; ok {
		return true
	}
	return false
}

// GetDir 获取一个可存放图片路径
func GetDir(scene string) (string, error) {
	year := time.Now().Year()
	month := time.Now().Month()
	day := time.Now().Day()
	create, err := tool.GetPathOrCreate(basePath, scene, year, month, day)
	if err != nil {
		return "", err
	}
	return create, nil
}

// Put 保存文件
func Put(scene string, filename string, size int64, data []byte) (string, error) {
	if !HasScene(scene) {
		return "", SceneNotExist
	} else {
		hhs := md5.New()
		hhs.Write(data)
		encodeToString := hex.EncodeToString(hhs.Sum(nil))
		has, err := db.Has([]byte(encodeToString), nil)
		if err != nil {
			return "", err
		}
		if !has {
			image := new(Image)
			image.Md5 = encodeToString
			image.Size = size
			image.FileName = filename
			image.Scene = scene
			image.CreateTime = time.Now().Unix()
			image.UpdateTime = time.Now().Unix()

			// 获取文件类型
			index := strings.LastIndex(filename, ".")
			// 无后缀的文件不支持
			if index == -1 {
				return "", ImageTypeNotSupport
			}
			// 不存在的文件不支持
			if image.Suffix = filename[index+1:]; !HasSuffix(image.Suffix) {
				return "", ImageTypeNotSupport
			}

			dir, err := GetDir(scene)
			if err != nil {
				return "", err
			}

			image.Path = dir + "/" + encodeToString + "." + image.Suffix

			err = ioutil.WriteFile(image.Path, data, 644)
			if err != nil {
				return "", err
			}

			marshal, err := json.Marshal(image)
			if err != nil {
				return "", err
			}
			err = db.Put([]byte(encodeToString), marshal, nil)
			if err != nil {
				// 如果数据库插入失败，则删除文件
				err := os.Remove(image.Path)
				if err != nil {
					return "", err
				}
				return "", err
			}

			return encodeToString, nil
		} else {
			//如果文件存在

			image, err := Get(encodeToString)
			if err != nil {
				return "", err
			}

			image.Size = size
			image.FileName = filename
			image.Scene = scene
			image.UpdateTime = time.Now().Unix()
			image.Md5 = encodeToString

			marshal, err := json.Marshal(image)
			if err != nil {
				return "", err
			}

			err = db.Put([]byte(encodeToString), marshal, nil)
			if err != nil {
				// 如果数据库插入失败，则删除文件
				err := os.Remove(image.Path)
				if err != nil {
					return "", err
				}
				return "", err
			}

			return encodeToString, nil
		}
	}
}

func DeleteImage(image *Image) error {
	if image != nil {
		err := os.Remove(image.Path)
		if err != nil {
			return err
		}

		err = db.Delete([]byte(image.Md5), nil)
		if err != nil {
			return err
		}
		return nil
	} else {
		return NullPtr
	}
}

// Get 获取文件信息
func Get(md5 string) (*Image, error) {
	get, err := db.Get([]byte(md5), nil)
	if err != nil {
		return nil, err
	}
	image := new(Image)
	err = json.Unmarshal(get, image)
	if err != nil {
		return nil, err
	}
	return image, nil
}

// GetImage 获取文件信息和全部byte
func GetImage(md5 string) (*Image, []byte, error) {
	image, err := Get(md5)
	if err != nil {
		return nil, nil, err
	}
	file, err := ioutil.ReadFile(image.Path)
	if err != nil {
		return nil, nil, err
	}
	return image, file, nil
}

// GetPartImage 获取文件信息和部分byte
func GetPartImage(md5 string, start, end int64) (*Image, []byte, error) {

	if start < 0 || end < 0 || end < start {
		return nil, nil, errors.New("param error")
	}

	image, err := Get(md5)
	if err != nil {
		return nil, nil, err
	}
	file, err := os.Open(image.Path)
	if err != nil {
		return nil, nil, err
	}
	_, err = file.Seek(start, 0)
	if err != nil {
		return nil, nil, err
	}

	if end > image.Size {
		end = image.Size
	}

	buff := make([]byte, 0, end-start)
	buffer := bytes.NewBuffer(buff)

	n := 512
	buf := make([]byte, n)
	for {
		if end-start+1 < int64(n) {
			n = int(end - start + 1)
		}
		_, err := file.Read(buf[:n])
		if err != nil {
			if err != io.EOF {
				log.Println("error:", err)
			}
			break
		}
		err = nil
		_, err = buffer.Write(buf[:n])
		if err != nil {
			break
		}
		start += int64(n)
		if start >= end+1 {
			break
		}
	}

	return image, buffer.Bytes(), nil
}

// GetScenes 获取所有场景
func GetScenes() []string {
	scene := make([]string, 0, len(scenes))
	for k, _ := range scenes {
		scene = append(scene, k)
	}
	return scene
}

// SearchImages 按照条件搜索
func SearchImages(keyword string) []*Image {
	images := make([]*Image, 0, 10)
	iter := db.NewIterator(nil, nil)
	for iter.Next() {
		image := new(Image)
		err := json.Unmarshal(iter.Value(), image)
		if err != nil {
			continue
		}

		if strings.Contains(image.FileName, keyword) {
			images = append(images, image)
		}
	}
	return images
}
